/* print.c
 * Routines for printing packet analysis trees.
 *
 * Gilbert Ramirez <gram@alumni.rice.edu>
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <glib.h>

#include <array.h>

#include <utf8_entities.h>
#include <strutil.h>
#include "wmem_strbuf.h"
#include "packet_info.h"
#include <ftypes.h>

#include "proto.h"

#include "lua_dissect.h"
#include "lua_dissector.h"

#include <print.h>

/*
 * Find the data source for a specified field, and return a pointer
 * to the data in it. Returns NULL if the data is out of bounds.
 */
/* XXX: What am I missing ?
 *      Why bother searching for fi->ds_tvb for the matching tvb
 *       in the data_source list ?
 *      IOW: Why not just use fi->ds_tvb for the arg to tvb_get_ptr() ?
 */

static const uint8_t *get_field_data(GSList *src_list, field_info *fi)
{
    GSList   *src_le;
    tvbuff_t *src_tvb;
    int       length, tvbuff_length;
    struct data_source *src;

    for (src_le = src_list; src_le != NULL; src_le = src_le->next) {
        src = (struct data_source *)src_le->data;
        src_tvb = get_data_source_tvb(src);
        if (fi->ds_tvb == src_tvb) {
            /*
             * Found it.
             *
             * XXX - a field can have a length that runs past
             * the end of the tvbuff.  Ideally, that should
             * be fixed when adding an item to the protocol
             * tree, but checking the length when doing
             * that could be expensive.  Until we fix that,
             * we'll do the check here.
             */
            tvbuff_length = tvb_captured_length_remaining(src_tvb,
                                                 fi->start);
            if (tvbuff_length < 0) {
                return NULL;
            }
            length = fi->length;
            if (length > tvbuff_length)
                length = tvbuff_length;
            return tvb_get_ptr(src_tvb, fi->start, length);
        }
    }
    return NULL;  /* not found */
}

static char *get_field_hex_value(GSList *src_list, field_info *fi)
{
    const uint8_t *pd;

    if (!fi->ds_tvb)
        return NULL;

    if (fi->length > tvb_captured_length_remaining(fi->ds_tvb, fi->start)) {
        return g_strdup("field length invalid!");
    }

    /* Find the data for this field. */
    pd = get_field_data(src_list, fi);

    if (pd) {
        int        i;
        char      *buffer;
        char      *p;
        int        len;
        const int  chars_per_byte = 2;

        len    = chars_per_byte * fi->length;
        buffer = (char *)g_malloc(sizeof(char)*(len + 1));
        buffer[len] = '\0'; /* Ensure NULL termination in bad cases */
        p = buffer;
        /* Print a simple hex dump */
        for (i = 0 ; i < fi->length; i++) {
            snprintf(p, chars_per_byte+1, "%02x", pd[i]);
            p += chars_per_byte;
        }
        return buffer;
    } else {
        return NULL;
    }
}

/* Returns an g_malloced string */
char* get_node_field_value(field_info* fi, lua_dissector_t* edt)
{
    if (fi->hfinfo->id == hf_text_only) {
        /* Text label.
         * Get the text */
        if (fi->rep) {
            return g_strdup(fi->rep->representation);
        }
        else {
            return get_field_hex_value(edt->pi.data_src, fi);
        }
    }
//    else if (fi->hfinfo->id == proto_data) {
//        /* Uninterpreted data, i.e., the "Data" protocol, is
//         * printed as a field instead of a protocol. */
//        return get_field_hex_value(edt->pi.data_src, fi);
//    }
    else {
        /* Normal protocols and fields */
        char       *dfilter_string;

        switch (fi->hfinfo->type)
        {
        case FT_PROTOCOL:
            /* Print out the full details for the protocol. */
            if (fi->rep) {
                return g_strdup(fi->rep->representation);
            } else {
                /* Just print out the protocol abbreviation */
                return g_strdup(fi->hfinfo->abbrev);
            }
        case FT_NONE:
            /* Return "1" so that the presence of a field of type
             * FT_NONE can be checked when using -T fields */
            return g_strdup("1");
        case FT_UINT_BYTES:
        case FT_BYTES:
            {
                char *ret;
                const uint8_t *bytes = fvalue_get_bytes_data(fi->value);
                if (bytes) {
                    dfilter_string = (char *)wmem_alloc(3*fvalue_length2(fi->value));
                    switch (fi->hfinfo->display) {
                    case SEP_DOT:
                        ret = bytes_to_hexstr_punct(dfilter_string, bytes, fvalue_length2(fi->value), '.');
                        break;
                    case SEP_DASH:
                        ret = bytes_to_hexstr_punct(dfilter_string, bytes, fvalue_length2(fi->value), '-');
                        break;
                    case SEP_COLON:
                        ret = bytes_to_hexstr_punct(dfilter_string, bytes, fvalue_length2(fi->value), ':');
                        break;
                    case SEP_SPACE:
                        ret = bytes_to_hexstr_punct(dfilter_string, bytes, fvalue_length2(fi->value), ' ');
                        break;
                    case BASE_NONE:
                    default:
                        ret = bytes_to_hexstr(dfilter_string, bytes, fvalue_length2(fi->value));
                        break;
                    }
                    *ret = '\0';
                    ret = g_strdup(dfilter_string);
                    wmem_free(dfilter_string);
                } else {
                    if (fi->hfinfo->display & BASE_ALLOW_ZERO) {
                        ret = g_strdup("<none>");
                    } else {
                        ret = g_strdup("<MISSING>");
                    }
                }
                return ret;
            }
            break;
        default:
            dfilter_string = fvalue_to_string_repr(fi->value, FTREPR_DISPLAY, fi->hfinfo->display);
            if (dfilter_string != NULL) {
                char* ret = g_strdup(dfilter_string);
                wmem_free(dfilter_string);
                return ret;
            } else {
                return get_field_hex_value(edt->pi.data_src, fi);
            }
        }
    }
}
